// 步长
var STEP = 20;

// 一共有18行10列
var ROW_COUNT = 18;
var COL_COUNT = 10;

// 构建数据源
var dataModel = [{
    // 躺着的L型
    0: {
      row: 2,
      col: 0
    },
    1: {
      row: 2,
      col: 1
    },
    2: {
      row: 2,
      col: 2
    },
    3: {
      row: 1,
      col: 2
    }
  },
  {
    // 凸型
    0: {
      row: 1,
      col: 1
    },
    1: {
      row: 0,
      col: 0
    },
    2: {
      row: 1,
      col: 0
    },
    3: {
      row: 2,
      col: 0
    }
  },
  {
    // 田型
    0: {
      row: 1,
      col: 1
    },
    1: {
      row: 2,
      col: 1
    },
    2: {
      row: 1,
      col: 2
    },
    3: {
      row: 2,
      col: 2
    }
  },
  {
    // 一字型
    0: {
      row: 0,
      col: 0
    },
    1: {
      row: 0,
      col: 1
    },
    2: {
      row: 0,
      col: 2
    },
    3: {
      row: 0,
      col: 3
    }
  },
  {
    // z型
    0: {
      row: 1,
      col: 1
    },
    1: {
      row: 1,
      col: 2
    },
    2: {
      row: 2,
      col: 2
    },
    3: {
      row: 2,
      col: 3
    }
  }

];

// 定义一个定时器
var mInterval = null;

// 确定固定元素的对象位置
// 使用键值对的形式存储
// k=行_列，v=块元素
var fixedBlocks = {};

// 当前数据源
var currentModel = {};

// 16宫格的初始位置
var grid_16_x = 0;
var grid_16_y = 0;

// 函数入口
function init() {
  onkeyDown();
  createBlock();
}

// 操作方块移动
function onkeyDown() {
  // 监听页面按键事件
  document.onkeydown = function(event) {
    // console.log(event.keyCode);
    switch (event.keyCode) {
      case 37:
        console.log('左');
        move(-1, 0);
        break;
      case 38:
        console.log('上');
        // move(0, -1);
        rotate();
        break;
      case 39:
        console.log('右');
        move(1, 0);
        break;
      case 40:
        console.log('下');
        move(0, 1);
        break;
      default:
        break;
    }
  }
}

// 控制块元素的移动
function move(x, y) {

  // 获取需要移动的元素
  // var moveItem = document.querySelector('.blockItem');
  // // 设置top 和left
  // moveItem.style.top = parseInt(moveItem.style.top || 0) + y * STEP + 'px';
  // moveItem.style.left = parseInt(moveItem.style.left || 0) + x * STEP + 'px';


  // 在元素移动时，判断是否会碰撞

  if (isMeet(grid_16_x + x, grid_16_y + y, currentModel)) {
    // 解决底部触碰
    if (y !== 0) {
      fixedBottomModel();
    }
    return;
  }
  // 移动的话 是整个16宫格在移动
  grid_16_x += x;
  grid_16_y += y;


  // 重现渲染一下数据模型的位置
  locationBlock();
}

// 构建数据源（即数据模型）
// 动态生成块元素
function createBlock() {
  // 在创建模型前，需要判断是否结束游戏
  if (isGameOver()) {
    gameOver();
    return;
  }
  // 当前数据模型（躺着的L型）
  // 随机生成模型 

  currentModel = dataModel[_.random(0, dataModel.length - 1)];
  grid_16_x = 0;
  grid_16_y = 0;
  // currentModel = dataModel[0];
  // 对象无法直接用length 来获取长度
  for (let i = 0; i < Object.keys(currentModel).length; i++) {
    var el = document.createElement('div');
    el.className = 'blockItem';
    document.getElementById('container').appendChild(el);
    // getElementById获取id 不需要再加#号了
  }
  // 设置方块位置
  locationBlock();
  // 在渲染完成模块的位置之后，让模型自由降落
  autoDown();
}

// 模型中每个方块的位置
function locationBlock() {
  // 定位之前先判断一下是否有方块元素超出容器
  checkBound();
  // currentModel = dataModel[0];
  var els = document.getElementsByClassName('blockItem');
  for (let i = 0; i < els.length; i++) {
    var activityModelEle = els[i];
    activityModelEle.style.top = (grid_16_y + currentModel[i].row) * STEP + 'px';
    activityModelEle.style.left = (grid_16_x + currentModel[i].col) * STEP + 'px';
  }
}

// 模型的旋转
function rotate() {
  // 移动之后的行 = 移动前的列
  // 移动之后的列 = 3 - 移动前的行

  // 得到数据源
  // currentModel = dataModel[0];
  // 克隆currentModel

  var cloneCurrentModel = _.cloneDeep(currentModel);

  for (let key in cloneCurrentModel) {
    let item = cloneCurrentModel[key];
    let temp = item.row;
    item.row = item.col;
    item.col = 3 - temp;
  }
  if (isMeet(grid_16_x, grid_16_y, cloneCurrentModel)) {
    return;
  }
  // 接受这个旋转
  currentModel = cloneCurrentModel;
  // 再重新渲染一下模型位置
  locationBlock();
}

// 控制模型在容器中运动
function checkBound() {
  // 定义边界
  var leftBound = 0;
  var rightBound = COL_COUNT;
  var bottomBound = ROW_COUNT;
  // currentModel = dataModel[0];
  for (let key in currentModel) {
    // 当元素超出范围，16宫格后退一步
    // 左边越界
    let item = currentModel[key];
    if (item.col + grid_16_x < leftBound) {
      grid_16_x++;
    }
    // 右边越界
    if (item.col + grid_16_x >= rightBound) {
      grid_16_x--;
    }

    // 底部越界
    if (item.row + grid_16_y >= bottomBound) {
      grid_16_y--;
      fixedBottomModel();
    }
  }
}

// 把模型控制在底部
function fixedBottomModel() {
  // 模型样式改变
  // 创建新的模型
  var els = document.getElementsByClassName('blockItem');
  for (let i = els.length - 1; i >= 0; i--) {

    var activityModelEle = els[i];
    // 更改块级元素的类名
    activityModelEle.className = 'fixedBottom';
    var blockModel = currentModel[i];
    // 记录固定在底部的方块的位置
    fixedBlocks[(grid_16_y + blockModel.row) + '_' + (grid_16_x + blockModel.col)] = activityModelEle;
  }
  // 记录固定元素的位置

  // 打印一下fixedBlocks
  // for (key in fixedBlocks) {
  //   console.log(key + '-------' + fixedBlocks[key]);
  //   return;
  // }
  // 判断该行是否被铺满
  isRemoveLine();
  // 创建新的模型前的16宫格要初始化
  createBlock();
}

// 判断模型之间的触碰问题
// x,y表示16宫格《将要》移动到的位置
// model 表示当前模型数据源《将要》完成的变化
function isMeet(x, y, model) {
  // 所谓模型之间的触碰，在一个固定的位置已经存在一个固定的块元素时，那么活动中的模型不可以再占用该位置
  // 判断触碰，就是在判断活动中的模型《将要移动到的位置》是否已经存在被固定的模型（块元素）了，如果
  // 存在返回true 表示将要移动到的位置会发生触碰，否则返回false
  // console.log(model);
  for (var key in model) {
    // console.log('key' + key);
    var blockModel = model[key];
    let str = (y + blockModel.row) + '_' + (x + blockModel.col);
    if (fixedBlocks[str]) {
      return true;
    }
  }
  return false;
}

// 判断一行是否被铺满
function isRemoveLine() {
  // 判断一行被铺满也就是一行中的每一列都有元素存在
  // 遍历每一行
  // 遍历每一列
  for (var i = 0; i < ROW_COUNT; i++) {
    // 假设该行被铺满
    var flag = true;
    // 遍历每一列
    for (var j = 0; j < COL_COUNT; j++) {
      if (!fixedBlocks[i + '_' + j]) {
        flag = false;
        break;
      }
    }
    if (flag == true) {
      // 表示该行已经被铺满了
      console.log('该行已经被铺满了');
      removeLine(i);
      downLine(i);
    }
  }
}

// 清空铺满的一行
// line 表示铺满的那一行数
function removeLine(line) {
  // 找到铺满的一行，遍历那一列
  // 清除数据源
  for (var i = 0; i < COL_COUNT; i++) {
    // 删除该行中的所有块元素
    document.getElementById('container').removeChild(fixedBlocks[line + '_' + i]);
    // 删除该行中的所有数据源
    fixedBlocks[line + '_' + i] = null;
  }
}

// 控制被清理之上的块元素下落
function downLine(line) {
  // 被清理行之上的所有块元素的数据源所在的函数+1
  // 让块元素在容器中的位置下落
  // 清理掉之前的块元素
  // 遍历铺满行之上的行
  for (var i = line - 1; i >= 0; i--) {
    // 遍历该行下的所有列
    for (var j = 0; j < COL_COUNT; j++) {
      // 如果不存在数据，则结束这一轮循环
      if (!fixedBlocks[i + '_' + j]) continue;
      // 存在数据的话
      // 被清理行之上的所有块元素的数据源所在的函数+1
      fixedBlocks[(i + 1) + '_' + j] = fixedBlocks[i + '_' + j];
      // 让块元素在容器中的位置下落
      fixedBlocks[(i + 1) + '_' + j].style.top = (i + 1) * STEP + 'px';
      // 清理掉之前的块元素
      fixedBlocks[i + '_' + j] = null;
    }
  }
}

// 让方块自由降落
function autoDown() {
  if (mInterval) {
    // 如果定时器存在的话，则清楚定时器
    clearInterval(mInterval);
  }
  mInterval = setInterval(() => {
    move(0, 1);
    // 往下移动
  }, 1000)
}

// 判断游戏是否结束
function isGameOver() {
  // 当第0行元素上存在块元素的时候，则可以判断游戏结束
  for (var i = 0; i < COL_COUNT; i++) {
    if (fixedBlocks['0_' + i]) {
      return true;
    }
  }
  return false;
}

// 游戏结束
function gameOver() {
  // 清除定时器
  // 弹出对话框告知对方游戏结束
  if (mInterval) {
    clearInterval(mInterval);
  }
  alert("游戏结束，刷新重玩");
}